#ifndef _SESSION_
#define _SESSION_

#include"util.hpp"

class Session;

using session_ptr=std::shared_ptr<Session>;

#define SESSION_FOREVER -1
#define SESSION_TIMEOUT 30000

enum user_statu
{
    UNLOGIN,LOGIN
};

// session对象实现
class Session
{
public:
    Session(const uint64_t &ssid):_ssid(ssid)
    {
        INF_LOG("session:%d create",_ssid);
    }

    // 获取会话ssid
    uint64_t ssid(){return _ssid;}

    // 设置会话状态
    void set_statu(const user_statu &statu){_statu=statu;}

    // 设置会话用户id
    void set_user(const uint64_t &uid){_uid=uid;}

    // 获取会话用户id
    uint64_t get_user(){return _uid;}

    // 判断用户是否在线
    bool is_login(){return _statu==LOGIN;}

    // 设置定时器
    void set_timer(const wsserver_t::timer_ptr &tp){_tp=tp;}

    // 获取定时器
    wsserver_t::timer_ptr get_timer(){return _tp;}

    ~Session()
    {
        INF_LOG("session %p destroy");
    }
private:
    uint64_t _ssid;     //session id
    uint64_t _uid;      //用户 id
    user_statu _statu;       //用户状态
    wsserver_t::timer_ptr _tp;  //session关联的定时器
};

class Session_Manager
{
public:
    Session_Manager(wsserver_t* srv):_ssid(1),_server(srv)
    {
        INF_LOG("session manager init");
    }

    // 创建新session
    session_ptr create_session(uint64_t uid,user_statu statu)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        session_ptr ssp(new Session(_ssid));
        ssp->set_statu(statu);
        ssp->set_user(uid);
        _session.insert(std::make_pair(_ssid,ssp));
        _ssid++;
        return ssp;
    }

    // 添加session
    void append_session(const session_ptr &ssp)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _session.insert(std::make_pair(ssp->ssid(),ssp));
    }

    // 通过ssid获取session
    session_ptr get_session_by_ssid(uint64_t ssid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it=_session.find(ssid);
        // 没找到返回空
        if(it==_session.end())
        {
            return session_ptr();
        }
        return it->second;
    }

    // 删除会话
    void remove_session(uint64_t ssid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _session.erase(ssid);
    }

    // 设置session到期时间
    void set_session_expire_time(uint64_t ssid,int ms)
    {
        // 通过ssid找到session
        session_ptr ssp=get_session_by_ssid(ssid);
        if(ssp.get()==nullptr)
        {
            return;
        }

        wsserver_t::timer_ptr tp=ssp->get_timer();
        // 获取到session的定时器之后有四种情况：
        // 1.会话永久存在，并要设置永久存在
        // 2.会话永久存在，并要设置定时删除
        // 3.会话已经被设置为定时删除，并要设置为永久存在
        // 4.会话已经被设置为定时删除，并要重新设置为定时删除，刷新定时时间
        if(tp.get()==nullptr&&ms==SESSION_FOREVER)
        {
            return;
        }
        else if(tp.get()==nullptr&&ms!=SESSION_FOREVER)
        {
            // 创建一个定时删除的定时器，并设置给session
            wsserver_t::timer_ptr tmp_tp=_server->set_timer(ms,std::bind(&Session_Manager::remove_session,this,ssid));
            ssp->set_timer(tmp_tp);
        }
        else if(tp.get()!=nullptr&&ms==SESSION_FOREVER)
        {
            // asio::steady_timer的定时器，在被取消后，会导致任务直接被执行
            // 但是回调任务处于任务队列中，并不会立刻执行
            tp->cancel();

            // 这时候可以，将会话定时器设置为空，之后再次将会话插入管理表中
            ssp->set_timer(wsserver_t::timer_ptr());
            _server->set_timer(0,std::bind(&Session_Manager::append_session,this,ssp));
        }
        else if(tp.get()!=nullptr&&ms!=SESSION_FOREVER)
        {
            tp->cancel();

            ssp->set_timer(wsserver_t::timer_ptr());
            _server->set_timer(0,std::bind(&Session_Manager::append_session,this,ssp));

            // 重新加入之后，再次设定回调删除任务
            wsserver_t::timer_ptr tmp_tp=_server->set_timer(ms,std::bind(&Session_Manager::remove_session,this,ssid));
            ssp->set_timer(tmp_tp);
        }
    }

    ~Session_Manager()
    {
        INF_LOG("session manager destroy");
    }
private:
    uint64_t _ssid;     //下一个创建的session id
    std::mutex _mutex;
    std::unordered_map<uint64_t,session_ptr> _session;
    wsserver_t* _server;
};

#endif